using System;
using System.Data;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml;

namespace OpenComposite.EII.XSDFormGenerator
{
	/// <summary>
	/// Summary description for dlgXPathResult.
	/// </summary>
	public class DlgXPathResult : System.Windows.Forms.Form
	{
		private System.Windows.Forms.ListView lvResults;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		// These are all used by the recursive routines:
		//	ProcessChildHeaders
		//	ProcessChildData
		private Hashtable cols=new Hashtable();
		private Hashtable colHasData=new Hashtable();
		private Hashtable colData=new Hashtable();
		private Hashtable colName=new Hashtable();
		private int colIdx=0;
		private ListViewItem lvi=null;
		private int rows=-1;
		private string[] colCreatesNewRow;
		private ArrayList newRowAL;
		
		public DlgXPathResult(XmlNodeList nodeList, string newRowOn)
		{
			InitializeComponent();

			colCreatesNewRow=newRowOn.Split(new char[] {' '});
			newRowAL=new ArrayList(colCreatesNewRow);

			// get a list of all the column headers in the node list
			foreach(XmlNode node in nodeList)
			{
				ProcessChildHeaders(null, node); //, node.FirstChild);
			}

			if (colCreatesNewRow.Length==0)
			{
				colCreatesNewRow=new string[] {(string)colName[0]};
			}

			// Fill the rows with data
//			lvi=CreateLVI(cols.Count);
//			int n=0;
			bool newRowRequired=false;
			foreach(XmlNode node in nodeList)
			{
				// this test creates a new row whenever the number of children rows returned is less than the 
				// previous # of children rows.  In the forms.xml file, this allows us to create the proper data
				// set for the XPath query: 
				//  //ControlSet[Name="Test"]/ControlInfo/Name | //ControlSet[Name="Test"]/ControlInfo/Control | //ControlSet[Name="Test"]/ControlInfo/Caption | //ControlSet[Name="Test"]/ControlInfo/VarName | //ControlSet[Name="Test"]/ControlInfo/ListName | //ControlSet[Name="Test"]/ControlInfo/ControlDimensions
				// Eventually, this will probably have to be upgraded to a pre-processor that knows when to force new rows
/*
				int q=node.ChildNodes.Count;
				if (q < n)
				{
					lvi=CreateLVI(cols.Count);
				}
				n=q;
*/				
				if (newRowRequired)
				{
					lvi=CreateLVI(cols.Count);
					newRowRequired=false;
				}
				ProcessAttributes(node);
				newRowRequired=ProcessChildData1(node, node.FirstChild, newRowRequired);
			}
		}

		#region Process Headers
		private void ProcessChildHeaders(XmlNode node, XmlNode child)
		{
			while (child != null)
			{
				// process attributes
				if (child.Attributes != null)
				{
					foreach(XmlAttribute attr in child.Attributes)
					{
						if (!cols.Contains(attr.Name))
						{
							cols.Add(attr.Name, colIdx);
							colHasData[colIdx]=false;
							colName[colIdx]=attr.Name;
							lvResults.Columns.Add(attr.Name, -2, HorizontalAlignment.Left);
							++colIdx;
						}
					}
				}

				// if this child is an element, get its children
				if (child.FirstChild is XmlElement)
				{
					ProcessChildHeaders(node, child.FirstChild);
				}
				else
				{
					// if not, then either it or its child is a text element
					string name=child.Name=="#text" ? node.Name : child.Name;
					if (!cols.Contains(name))
					{
						// add the column header
						cols.Add(name, colIdx);
						colHasData[colIdx]=false;
						colName[colIdx]=name;
						lvResults.Columns.Add(name, -2, HorizontalAlignment.Left);
						++colIdx;
					}
				}
				child=child.NextSibling;
			}
		}
		#endregion

		#region Process Data
		private bool ProcessChildData1(XmlNode node, XmlNode child, bool newRowRequired)
		{
//			bool newRowRequired=false;
			if (newRowRequired)
			{
				lvi=CreateLVI(cols.Count);
				newRowRequired=false;
			}
			while (child != null)
			{
				ProcessAttributes(child);

				// if this child is an element, get its children
				if (child.FirstChild is XmlElement)
				{
					newRowRequired|=ProcessChildData1(child, child.FirstChild, newRowRequired);
					ProcessChildData2();
//					if (newRowRequired)
//					{
//						lvi=CreateLVI(cols.Count);
//						newRowRequired=false;
//					}
				}
				else
				{
					// if not, then either it or its child is a text element.
					string name=child.Name=="#text" ? node.Name : child.Name;
					colData[name]=child.InnerText;
					if (newRowAL.Contains(name))
					{
						newRowRequired=true;
					}
				}
				ProcessChildData2();
				child=child.NextSibling;
			}
			return newRowRequired;
		}

		private void ProcessChildData2()
		{
			if (lvi==null)
			{
				lvi=CreateLVI(cols.Count);
			}
			IDictionaryEnumerator iter=colData.GetEnumerator();
			while (iter.MoveNext())
			{
				string key=(string)iter.Key;
				string val=(string)iter.Value;
				int n=(int)cols[key];
				if (lvi.SubItems[n].Text != "")
				{
					lvi=CreateLVI(cols.Count);
					break;
				}
			}

			iter.Reset();
			while (iter.MoveNext())
			{
				string key=(string)iter.Key;
				string val=(string)iter.Value;
				int n=(int)cols[key];
				lvi.SubItems[n].Text=val;
			}

			colData.Clear();
		}

		#endregion

		#region Process Attributes
		private void ProcessAttributes(XmlNode node)
		{
			// process attributes
			if (node.Attributes != null)
			{
				foreach(XmlAttribute attr in node.Attributes)
				{
					colData[attr.Name]=attr.Value;
				}
			}
		}
		#endregion

		private ListViewItem CreateLVI(int n)
		{
			ListViewItem lvi=new ListViewItem();
			for (int i=0; i<n; i++)
			{
				lvi.SubItems.Add("");
			}
			lvResults.Items.Add(lvi);
			++rows;
			return lvi;
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.lvResults = new System.Windows.Forms.ListView();
			this.SuspendLayout();
			// 
			// lvResults
			// 
			this.lvResults.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
				| System.Windows.Forms.AnchorStyles.Left) 
				| System.Windows.Forms.AnchorStyles.Right);
			this.lvResults.AutoArrange = false;
			this.lvResults.GridLines = true;
			this.lvResults.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
			this.lvResults.Location = new System.Drawing.Point(8, 8);
			this.lvResults.MultiSelect = false;
			this.lvResults.Name = "lvResults";
			this.lvResults.Size = new System.Drawing.Size(672, 264);
			this.lvResults.TabIndex = 0;
			this.lvResults.View = System.Windows.Forms.View.Details;
			// 
			// DlgXPathResult
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(688, 278);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
															   this.lvResults});
			this.Name = "DlgXPathResult";
			this.Text = "XPath Result";
			this.ResumeLayout(false);

		}
		#endregion
	}
}

